#include "Direct3DFramework.h"

int Direct3DFramework::drawBegin()
{
	//clear back-buffer and z-buffer
	m_device->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,m_window->m_windowColor,1.0f,0);

	//begin drawing
	m_device->BeginScene();

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::drawEnd()
{
	//end drawing
	m_device->EndScene();

	//present the drawing
	m_device->Present(0,0,0,0);

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::drawQuad()
{
	//buffer variables to gpu
	LPD3DXEFFECT effect = m_cEffect->m_effect;
	effect->SetTechnique(m_cEffect->m_technique);
	if(m_cEffect->m_fxCamPos)      effect->SetValue(m_cEffect->m_fxCamPos,&m_camPos,sizeof(D3DXVECTOR3));
	if(m_cEffect->m_fxLitPos)      effect->SetValue(m_cEffect->m_fxLitPos,&m_cLight.m_pos,sizeof(V3DF));
	if(m_cEffect->m_fxLitAmbient)  effect->SetValue(m_cEffect->m_fxLitAmbient,&m_cLight.m_ambient,sizeof(DColor));
	if(m_cEffect->m_fxLitSpecular) effect->SetValue(m_cEffect->m_fxLitSpecular,&m_cLight.m_specular,sizeof(DColor));
	if(m_cEffect->m_fxMatAmbient)  effect->SetValue(m_cEffect->m_fxMatAmbient,&m_cMaterial.m_ambient,sizeof(DColor));
	if(m_cEffect->m_fxMatDiffuse)  effect->SetValue(m_cEffect->m_fxMatDiffuse,&m_cMaterial.m_diffuse,sizeof(DColor));
	if(m_cEffect->m_fxMatSpecular) effect->SetValue(m_cEffect->m_fxMatSpecular,&m_cMaterial.m_specular,sizeof(DColor));
	if(m_cEffect->m_fxMatEmissive) effect->SetValue(m_cEffect->m_fxMatEmissive,&m_cMaterial.m_emissive,sizeof(DColor));
	if(m_cEffect->m_fxMatPower)    effect->SetFloat(m_cEffect->m_fxMatPower,m_cMaterial.m_power);
	if(m_cEffect->m_fxTexture2DA)  effect->SetTexture(m_cEffect->m_fxTexture2DA,m_cTexture2D[0]);
	if(m_cEffect->m_fxTexture2DB)  effect->SetTexture(m_cEffect->m_fxTexture2DB,m_cTexture2D[1]);
	if(m_cEffect->m_fxTexture2DC)  effect->SetTexture(m_cEffect->m_fxTexture2DC,m_cTexture2D[2]);
	if(m_cEffect->m_fxTexture2DD)  effect->SetTexture(m_cEffect->m_fxTexture2DD,m_cTexture2D[3]);
	if(m_cEffect->m_fxTexture2DE)  effect->SetTexture(m_cEffect->m_fxTexture2DA,m_cTexture2D[4]);
	if(m_cEffect->m_fxTexture2DF)  effect->SetTexture(m_cEffect->m_fxTexture2DB,m_cTexture2D[5]);
	if(m_cEffect->m_fxTexture3DA)  effect->SetTexture(m_cEffect->m_fxTexture3DA,m_cTexture3D[0]);
	if(m_cEffect->m_fxTexture3DB)  effect->SetTexture(m_cEffect->m_fxTexture3DB,m_cTexture3D[1]);
	if(m_cEffect->m_fxTexture3DC)  effect->SetTexture(m_cEffect->m_fxTexture3DC,m_cTexture3D[2]);
	if(m_cEffect->m_fxTexture3DD)  effect->SetTexture(m_cEffect->m_fxTexture3DD,m_cTexture3D[3]);
	if(m_cEffect->m_fxTexture3DE)  effect->SetTexture(m_cEffect->m_fxTexture3DA,m_cTexture3D[4]);
	if(m_cEffect->m_fxTexture3DF)  effect->SetTexture(m_cEffect->m_fxTexture3DB,m_cTexture3D[5]);
	if(m_cEffect->m_fxWinWidth)    effect->SetFloat(m_cEffect->m_fxWinWidth,(float)m_window->m_windowWidth);
	if(m_cEffect->m_fxWinHeight)   effect->SetFloat(m_cEffect->m_fxWinHeight,(float)m_window->m_windowHeight);

	//begin drawing
	unsigned int passes = 0;
	effect->Begin(&passes,0);
	for(unsigned int i = 0; i < passes; i++)
	{
		effect->BeginPass(i);

		m_device->SetVertexDeclaration(m_vertDeclQuad);
		m_device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2,m_screenQuad,sizeof(D3DFQuad));

		effect->EndPass();
	}
	effect->End();

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::drawMesh(DMesh* a_mesh)
{
	//static variables
	static D3DXMATRIX world;
	static D3DXMATRIX worldVP;
	static D3DXMATRIX worldIT;
	static D3DXMATRIX worldI;
	static D3DXMATRIX viewI;

	static D3DXMATRIX litWorld;
	static D3DXMATRIX litView;

	//clear world matrices variables
	D3DXMatrixIdentity(&worldVP);
	D3DXMatrixIdentity(&worldIT);
	D3DXMatrixIdentity(&worldI);
	D3DXMatrixIdentity(&viewI);

	D3DXMatrixIdentity(&litWorld);
	D3DXMatrixIdentity(&litView);

	//obtain mesh world matrix
	world = m_meshList[a_mesh->m_meshId]->m_world[a_mesh->m_meshMat];

	//compute world view projection
	worldVP = world*m_view*m_proj;

	//compute world inverse transpose
	D3DXMatrixInverse(&worldI,0,&world);
	D3DXMatrixTranspose(&worldIT,&worldI);

	//compute view inverse
	D3DXMatrixInverse(&viewI,0,&m_view);

	//compute light matrix
	D3DXMatrixTranslation(&litWorld,m_cLight.m_pos.x,m_cLight.m_pos.y,m_cLight.m_pos.z);

	D3DXVECTOR3 litPos(m_cLight.m_pos.x,m_cLight.m_pos.y,m_cLight.m_pos.z);
	D3DXVECTOR3 litLook(0,0,0);
	D3DXVECTOR3 litUp(0,1,0);
	D3DXMatrixLookAtLH(&litView,&litPos,&litLook,&litUp);

	//effects variables to gpu
	LPD3DXEFFECT effect = m_cEffect->m_effect;
	effect->SetTechnique(m_cEffect->m_technique);
	if(m_cEffect->m_fxWorld)       effect->SetMatrix(m_cEffect->m_fxWorld,&world);
	if(m_cEffect->m_fxView)        effect->SetMatrix(m_cEffect->m_fxView,&m_view);
	if(m_cEffect->m_fxProj)        effect->SetMatrix(m_cEffect->m_fxProj,&m_proj);
	if(m_cEffect->m_fxWorldVP)     effect->SetMatrix(m_cEffect->m_fxWorldVP,&worldVP);
	if(m_cEffect->m_fxWorldIT)     effect->SetMatrix(m_cEffect->m_fxWorldIT,&worldIT);
	if(m_cEffect->m_fxWorldI)      effect->SetMatrix(m_cEffect->m_fxWorldI,&worldI);
	if(m_cEffect->m_fxViewI)       effect->SetMatrix(m_cEffect->m_fxViewI,&viewI);
	if(m_cEffect->m_fxCamPos)      effect->SetValue(m_cEffect->m_fxCamPos,&m_camPos,sizeof(D3DXVECTOR3));
	if(m_cEffect->m_fxLitPos)      effect->SetValue(m_cEffect->m_fxLitPos,&m_cLight.m_pos,sizeof(V3DF));
	if(m_cEffect->m_fxLitWorld)    effect->SetMatrix(m_cEffect->m_fxLitWorld,&litWorld);
	if(m_cEffect->m_fxLitView)     effect->SetMatrix(m_cEffect->m_fxLitView,&litView);
	if(m_cEffect->m_fxLitAmbient)  effect->SetValue(m_cEffect->m_fxLitAmbient,&m_cLight.m_ambient,sizeof(DColor));
	if(m_cEffect->m_fxLitSpecular) effect->SetValue(m_cEffect->m_fxLitSpecular,&m_cLight.m_specular,sizeof(DColor));
	if(m_cEffect->m_fxMatAmbient)  effect->SetValue(m_cEffect->m_fxMatAmbient,&m_cMaterial.m_ambient,sizeof(DColor));
	if(m_cEffect->m_fxMatDiffuse)  effect->SetValue(m_cEffect->m_fxMatDiffuse,&m_cMaterial.m_diffuse,sizeof(DColor));
	if(m_cEffect->m_fxMatSpecular) effect->SetValue(m_cEffect->m_fxMatSpecular,&m_cMaterial.m_specular,sizeof(DColor));
	if(m_cEffect->m_fxMatEmissive) effect->SetValue(m_cEffect->m_fxMatEmissive,&m_cMaterial.m_emissive,sizeof(DColor));
	if(m_cEffect->m_fxMatPower)    effect->SetFloat(m_cEffect->m_fxMatPower,m_cMaterial.m_power);
	if(m_cEffect->m_fxTexture2DA)  effect->SetTexture(m_cEffect->m_fxTexture2DA,m_cTexture2D[0]);
	if(m_cEffect->m_fxTexture2DB)  effect->SetTexture(m_cEffect->m_fxTexture2DB,m_cTexture2D[1]);
	if(m_cEffect->m_fxTexture2DC)  effect->SetTexture(m_cEffect->m_fxTexture2DC,m_cTexture2D[2]);
	if(m_cEffect->m_fxTexture2DD)  effect->SetTexture(m_cEffect->m_fxTexture2DD,m_cTexture2D[3]);
	if(m_cEffect->m_fxTexture2DE)  effect->SetTexture(m_cEffect->m_fxTexture2DA,m_cTexture2D[4]);
	if(m_cEffect->m_fxTexture2DF)  effect->SetTexture(m_cEffect->m_fxTexture2DB,m_cTexture2D[5]);
	if(m_cEffect->m_fxTexture3DA)  effect->SetTexture(m_cEffect->m_fxTexture3DA,m_cTexture3D[0]);
	if(m_cEffect->m_fxTexture3DB)  effect->SetTexture(m_cEffect->m_fxTexture3DB,m_cTexture3D[1]);
	if(m_cEffect->m_fxTexture3DC)  effect->SetTexture(m_cEffect->m_fxTexture3DC,m_cTexture3D[2]);
	if(m_cEffect->m_fxTexture3DD)  effect->SetTexture(m_cEffect->m_fxTexture3DD,m_cTexture3D[3]);
	if(m_cEffect->m_fxTexture3DE)  effect->SetTexture(m_cEffect->m_fxTexture3DA,m_cTexture3D[4]);
	if(m_cEffect->m_fxTexture3DF)  effect->SetTexture(m_cEffect->m_fxTexture3DB,m_cTexture3D[5]);
	if(m_cEffect->m_fxWinWidth)    effect->SetFloat(m_cEffect->m_fxWinWidth,(float)m_window->m_windowWidth);
	if(m_cEffect->m_fxWinHeight)   effect->SetFloat(m_cEffect->m_fxWinHeight,(float)m_window->m_windowHeight);

	//get the mesh from the list
	D3DFMesh* mesh = m_meshList[a_mesh->m_meshId];

	//begin drawing
	unsigned int passes = 0;
	effect->Begin(&passes,0);
	for(unsigned int i = 0; i < passes; i++)
	{
		effect->BeginPass(i);

		for(int j = 0; j < mesh->m_subs; j++)
		{
			if(mesh->m_texs[j] != -1)
			{
				D3DFTexture2D* tex = m_texture2DList[mesh->m_texs[j]];
				effect->SetTexture(m_cEffect->m_fxTexture2DA,tex->m_texture);
				effect->CommitChanges();
			}
			else
			{
				effect->SetTexture(m_cEffect->m_fxTexture2DA,m_cTexture2D[0]);
				effect->CommitChanges();
			}
			mesh->m_mesh->DrawSubset(j);
		}

		effect->EndPass();
	}
	effect->End();

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::drawTexture2D(DTexture2D* a_texture)
{
	D3DFTexture2D* texture = m_texture2DList[a_texture->m_texId];

	m_spriteQuad[0].x = -0.5f;
	m_spriteQuad[0].y = -0.5f;
	m_spriteQuad[1].x = (float)a_texture->m_texWidth  - 0.5f;
	m_spriteQuad[1].y = -0.5f;
	m_spriteQuad[2].x = -0.5f;
	m_spriteQuad[2].y = (float)a_texture->m_texHeight - 0.5f;
	m_spriteQuad[3].x = (float)a_texture->m_texWidth  - 0.5f;
	m_spriteQuad[3].y = (float)a_texture->m_texHeight - 0.5f;

	m_spriteQuad[0].x += texture->m_world[a_texture->m_texMat].m_cen.x;
	m_spriteQuad[0].y += texture->m_world[a_texture->m_texMat].m_cen.y;
	m_spriteQuad[1].x += texture->m_world[a_texture->m_texMat].m_cen.x;
	m_spriteQuad[1].y += texture->m_world[a_texture->m_texMat].m_cen.y;
	m_spriteQuad[2].x += texture->m_world[a_texture->m_texMat].m_cen.x;
	m_spriteQuad[2].y += texture->m_world[a_texture->m_texMat].m_cen.y;
	m_spriteQuad[3].x += texture->m_world[a_texture->m_texMat].m_cen.x;
	m_spriteQuad[3].y += texture->m_world[a_texture->m_texMat].m_cen.y;

	m_spriteQuad[0].x *= texture->m_world[a_texture->m_texMat].m_scl.x;
	m_spriteQuad[0].y *= texture->m_world[a_texture->m_texMat].m_scl.y;
	m_spriteQuad[1].x *= texture->m_world[a_texture->m_texMat].m_scl.x;
	m_spriteQuad[1].y *= texture->m_world[a_texture->m_texMat].m_scl.y;
	m_spriteQuad[2].x *= texture->m_world[a_texture->m_texMat].m_scl.x;
	m_spriteQuad[2].y *= texture->m_world[a_texture->m_texMat].m_scl.y;
	m_spriteQuad[3].x *= texture->m_world[a_texture->m_texMat].m_scl.x;
	m_spriteQuad[3].y *= texture->m_world[a_texture->m_texMat].m_scl.y;

	m_spriteQuad[0].x += texture->m_world[a_texture->m_texMat].m_trn.x;
	m_spriteQuad[0].y += texture->m_world[a_texture->m_texMat].m_trn.y;
	m_spriteQuad[1].x += texture->m_world[a_texture->m_texMat].m_trn.x;
	m_spriteQuad[1].y += texture->m_world[a_texture->m_texMat].m_trn.y;
	m_spriteQuad[2].x += texture->m_world[a_texture->m_texMat].m_trn.x;
	m_spriteQuad[2].y += texture->m_world[a_texture->m_texMat].m_trn.y;
	m_spriteQuad[3].x += texture->m_world[a_texture->m_texMat].m_trn.x;
	m_spriteQuad[3].y += texture->m_world[a_texture->m_texMat].m_trn.y;

	//buffer variables to gpu
	LPD3DXEFFECT effect = m_cEffect->m_effect;
	effect->SetTechnique(m_cEffect->m_technique);
	if(m_cEffect->m_fxTexture2DA)  effect->SetTexture(m_cEffect->m_fxTexture2DA,texture->m_texture);

	//begin drawing
	unsigned int passes = 0;
	effect->Begin(&passes,0);
	for(unsigned int i = 0; i < passes; i++)
	{
		effect->BeginPass(i);

		m_device->SetVertexDeclaration(m_vertDeclQuad);
		m_device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2,m_spriteQuad,sizeof(D3DFQuad));

		effect->EndPass();
	}
	effect->End();

	//return after completion
	return D3D_SUCCESS;
}

int Direct3DFramework::drawText(char* a_text, V2DF a_pos, int a_color)
{
	//create direct3d color with given packed color
	D3DCOLOR color = D3DCOLOR(a_color);

	//create text rect
	RECT rect = {0,0,0,0};
	m_font->DrawText(0,TEXT(a_text),-1,&rect,DT_CALCRECT,color);

	//translate position
	rect.left   += (long)a_pos.x;
	rect.top    += (long)a_pos.y;
	rect.right  += (long)a_pos.x;
	rect.bottom += (long)a_pos.y;

	//draw text
	m_font->DrawText(0,TEXT(a_text),-1,&rect,0,color);

	return D3D_SUCCESS;
}

int Direct3DFramework::surfaceSave()
{
	m_device->GetRenderTarget(0,&m_surfBack);

	return D3D_SUCCESS;
};

int Direct3DFramework::surfaceLoad()
{
	m_device->SetRenderTarget(0,m_surfBack);
	m_surfBack = 0;

	return D3D_SUCCESS;
};

int Direct3DFramework::surfaceClear()
{
	m_device->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,m_window->m_windowColor,1.0f,0);

	return D3D_SUCCESS;
};

int Direct3DFramework::surfaceSwap(DSurface* a_surfRT)
{
	if(a_surfRT == 0) return D3D_SUCCESS;

	m_device->SetRenderTarget(0,m_surfaceList[a_surfRT->m_surfId]->m_surface);

	return D3D_SUCCESS;
};